
/* Includes ------------------------------------------------------------------*/
#include "pwm.h"

/* Private typedef -----------------------------------------------------------*/
typedef struct
{
  void          (*TIM_SetCompare)(TIM_TypeDef*, uint32_t);
  TIM_TypeDef*  TIM;
  GPIO_TypeDef* GPIO_Port; 
  uint32_t      GPIO_Pin;
  uint16_t      GPIO_PinSource;
  uint8_t       GPIO_AF;
} pwm_led_config_line;
/* Private define ------------------------------------------------------------*/
#define NUM_OF_LED_PWM_CH 24
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

const pwm_led_config_line PWM_LED_config[] = 
{
  // Yellow group 1
  {TIM_SetCompare4, TIM2, GPIOA, GPIO_Pin_3,  GPIO_PinSource3,  GPIO_AF_1}, //yellow left up
  {TIM_SetCompare2, TIM2, GPIOA, GPIO_Pin_1,  GPIO_PinSource1,  GPIO_AF_1}, //yellow left down
  {TIM_SetCompare1, TIM2, GPIOA, GPIO_Pin_5,  GPIO_PinSource5,  GPIO_AF_1}, //yellow left right
  {TIM_SetCompare3, TIM2, GPIOA, GPIO_Pin_2,  GPIO_PinSource2,  GPIO_AF_1}, //yellow left left

  {TIM_SetCompare4, TIM4, GPIOD, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_2}, //yellow right up
  {TIM_SetCompare2, TIM4, GPIOD, GPIO_Pin_13, GPIO_PinSource13, GPIO_AF_2}, //yellow right down
  {TIM_SetCompare1, TIM4, GPIOD, GPIO_Pin_12, GPIO_PinSource12, GPIO_AF_2}, //yellow right right
  {TIM_SetCompare3, TIM4, GPIOD, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF_2}, //yellow right left

  // Red group 2
  {TIM_SetCompare4, TIM2, GPIOD, GPIO_Pin_6,  GPIO_PinSource6,  GPIO_AF_2}, //red left up
  {TIM_SetCompare2, TIM2, GPIOD, GPIO_Pin_4,  GPIO_PinSource4,  GPIO_AF_2}, //red left down
  {TIM_SetCompare1, TIM2, GPIOD, GPIO_Pin_3,  GPIO_PinSource3,  GPIO_AF_2}, //red left right
  {TIM_SetCompare3, TIM2, GPIOD, GPIO_Pin_7,  GPIO_PinSource7,  GPIO_AF_2}, //red left left

  {TIM_SetCompare4, TIM3, GPIOB, GPIO_Pin_1,  GPIO_PinSource1,  GPIO_AF_2}, //red right up
  {TIM_SetCompare2, TIM3, GPIOB, GPIO_Pin_5,  GPIO_PinSource5,  GPIO_AF_2}, //red right down
  {TIM_SetCompare1, TIM3, GPIOB, GPIO_Pin_4,  GPIO_PinSource4,  GPIO_AF_2}, //red right right
  {TIM_SetCompare3, TIM3, GPIOB, GPIO_Pin_0,  GPIO_PinSource0,  GPIO_AF_2}, //red right left

  // Green group 3
  {TIM_SetCompare2, TIM3, GPIOC, GPIO_Pin_7,  GPIO_PinSource7,  GPIO_AF_2}, //green left up
  {TIM_SetCompare1, TIM3, GPIOC, GPIO_Pin_6,  GPIO_PinSource6,  GPIO_AF_2}, //green left down
  {0              , 0   , 0    , 0         ,  0              ,  0        }, //green left right
  {0              , 0   , 0    , 0         ,  0              ,  0        }, //green left left

  {TIM_SetCompare4, TIM2, GPIOB, GPIO_Pin_11, GPIO_PinSource11, GPIO_AF_1}, //green right up
  {TIM_SetCompare2, TIM2, GPIOB, GPIO_Pin_3,  GPIO_PinSource3,  GPIO_AF_1}, //green right down
  {TIM_SetCompare1, TIM2, GPIOA, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_1}, //green right right
  {TIM_SetCompare3, TIM2, GPIOB, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF_1}  //green right left
};

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Configure the TIM IRQ Handler.
  * @param  None
  * @retval None
  */
void TIM_Config(void)
{
  uint8_t i;
  GPIO_InitTypeDef GPIO_InitStructure;
  TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;
  TIM_OCInitTypeDef         TIM_OCInitStructure;
  
  /* TIM clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
  
  /* GPIO clock enable*/
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
  
  /* Configure Alternate Function */
  GPIO_StructInit(&GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;

  for (i = 0; i < COUNT_OF(PWM_LED_config); i++)
  {
    if (PWM_LED_config[i].GPIO_Port && PWM_LED_config[i].GPIO_Pin)
    {
      GPIO_InitStructure.GPIO_Pin = PWM_LED_config[i].GPIO_Pin;
      GPIO_Init(PWM_LED_config[i].GPIO_Port, &GPIO_InitStructure);
      GPIO_PinAFConfig(PWM_LED_config[i].GPIO_Port, PWM_LED_config[i].GPIO_PinSource, PWM_LED_config[i].GPIO_AF);
    }
  }
  
  /* Initialize Timers*/  
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_OCStructInit(&TIM_OCInitStructure);

  /* Time base configuration for timers */
  TIM_TimeBaseStructure.TIM_Period = 255;
  TIM_TimeBaseStructure.TIM_Prescaler = 125;
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  /* Channel configuration for timers */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set;

  TIM_OC1Init(TIM2, &TIM_OCInitStructure);
  TIM_OC2Init(TIM2, &TIM_OCInitStructure);
  TIM_OC3Init(TIM2, &TIM_OCInitStructure);
  TIM_OC4Init(TIM2, &TIM_OCInitStructure);

  TIM_OC1Init(TIM3, &TIM_OCInitStructure);
  TIM_OC2Init(TIM3, &TIM_OCInitStructure);
  TIM_OC3Init(TIM3, &TIM_OCInitStructure);
  TIM_OC4Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1Init(TIM4, &TIM_OCInitStructure);
  TIM_OC2Init(TIM4, &TIM_OCInitStructure);
  TIM_OC3Init(TIM4, &TIM_OCInitStructure);
  TIM_OC4Init(TIM4, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
  TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
  TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
  TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);

  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
  TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

  TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
  TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
  TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
  TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
    
  /* TIM enable counter */
  TIM_Cmd(TIM2, ENABLE);
  TIM_Cmd(TIM3, ENABLE);
  TIM_Cmd(TIM4, ENABLE);
}

void set_led_pwm_group(uint8_t newgroup)
{
  uint8_t i;
  GPIO_InitTypeDef GPIO_InitStructure;
  
  /* Configure Alternate Function, disable all pwm outputs*/
  GPIO_StructInit(&GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;

  for (i = 0; i < COUNT_OF(PWM_LED_config); i++)
  {
    if (PWM_LED_config[i].GPIO_Port && PWM_LED_config[i].GPIO_Pin)
    {
      GPIO_InitStructure.GPIO_Pin = PWM_LED_config[i].GPIO_Pin;
      GPIO_Init(PWM_LED_config[i].GPIO_Port, &GPIO_InitStructure);
    }
  }

  // If group number is wrong, leave all outputs disabled
  if(newgroup > 3 || newgroup < 1)
  {
    //while(1);
    return;
  }

  // Otherwise, set only one group to AF
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  for (i = (newgroup - 1) * 8; i < newgroup * 8; i++)
  {
    if (PWM_LED_config[i].GPIO_Port && PWM_LED_config[i].GPIO_Pin)
    {
      GPIO_InitStructure.GPIO_Pin = PWM_LED_config[i].GPIO_Pin;
      GPIO_Init(PWM_LED_config[i].GPIO_Port, &GPIO_InitStructure);
    }
  }
}

void set_led_pwm_values(uint8_t group, uint8_t values[4])
{
  uint8_t i;
  uint8_t pwm_value_n, pwm_value_p; //lv, lh, rv, rh

  if(group > 3 || group < 1)
  {
    //while(1);
    return;
  }

  for(i = 0; i < 4; i++)
  {
    uint8_t idx = (group - 1) * 8 + i*2;
    const pwm_led_config_line *pwm_cfg;

    if (values[i] >= 128)
    {
      pwm_value_p = values[i] - 128;
      pwm_value_n = 0;
    }
    else
    {
      pwm_value_p = 0;
      pwm_value_n = 127 - values[i];
    }

    pwm_cfg = &PWM_LED_config[idx];
    if(pwm_cfg->TIM_SetCompare && pwm_cfg->TIM)
    {
      pwm_cfg->TIM_SetCompare(pwm_cfg->TIM, pwm_value_p);
    }

    pwm_cfg = &PWM_LED_config[idx + 1];
    if(pwm_cfg->TIM_SetCompare && pwm_cfg->TIM)
    {
      pwm_cfg->TIM_SetCompare(pwm_cfg->TIM, pwm_value_n);
    }
  }
}
